#include <mach/mach.h>
#include <mach-o/dyld.h>
#include <string.h>
#include <unistd.h>
#include <os/log.h>
#include <mach-o/loader.h>
#include <mach/mach_vm.h>
#include "lualib.h"

// Boyer-Moore 搜索算法
static void *search_bytes(const uint8_t *haystack, size_t haystack_len,
                          const uint8_t *needle, size_t needle_len)
{
    if (needle_len == 0)
        return (void *)haystack;
    if (haystack_len < needle_len)
        return NULL;
    int n = needle_len - 1;

    // 构建坏字符表
    size_t bad_char[256];
    for (size_t i = 0; i < 256; i++)
    {
        bad_char[i] = needle_len;
    }
    for (size_t i = 0; i < n; i++)
    {
        bad_char[needle[i]] = n - i;
    }

    // 搜索
    size_t offset = 0;
    while (offset <= haystack_len - needle_len)
    {
        int j = n;
        while (j >= 0 && needle[j] == haystack[offset + j])
        {
            j--;
        }
        if (j < 0)
        {
            return (void *)(haystack + offset);
        }
        offset += bad_char[haystack[offset + j]];
    }
    return NULL;
}

// 在 text 段中搜索
static void *find_bytes_in_text(const uint8_t *target, size_t target_len)
{
    // 获取 mach-o header
    const struct mach_header_64 *header = (const struct mach_header_64 *)_dyld_get_image_header(0);
    const struct load_command *cmd = (const struct load_command *)((uint8_t *)header + sizeof(struct mach_header_64));

    // 遍历加载命令找到 text 段
    for (uint32_t i = 0; i < header->ncmds; i++)
    {
        if (cmd->cmd == LC_SEGMENT_64)
        {
            const struct segment_command_64 *seg = (const struct segment_command_64 *)cmd;
            if (strcmp(seg->segname, "__TEXT") == 0)
            {
                // 遍历 sections
                const struct section_64 *sect = (const struct section_64 *)((uint8_t *)seg + sizeof(struct segment_command_64));
                for (uint32_t j = 0; j < seg->nsects; j++)
                {
                    if (strcmp(sect->sectname, "__text") == 0)
                    {
                        // 在 text section 中搜索
                        const uint8_t *text_start = (const uint8_t *)header + sect->offset;
                        return search_bytes(text_start, sect->size, target, target_len);
                    }
                    sect++;
                }
            }
        }
        cmd = (const struct load_command *)((uint8_t *)cmd + cmd->cmdsize);
    }
    return NULL;
}

// 在程序加载时自动执行的构造函数
void inject_entry_jhqjs()
{
    os_log_t log = os_log_create("com.example.app", "general");
    os_log(log, "注入库已加载到目标进程。PID: %d", getpid());

    // 计算偏移地址
    const uint8_t needle[] = {0x4a, 0x8b, 0x34, 0x3b, 0x4c, 0x89, 0xf7, 0x31, 0xd2};
    void *ret = find_bytes_in_text(needle, sizeof(needle));
    if (ret == NULL)
    {
        os_log(log, "未找到匹配");
        return;
    }
    mach_vm_address_t target_address = (uint64_t)(ret + *(uint32_t *)(ret - 4) + 0x10);
    os_log(log, "找到匹配: 0x%llx", target_address);

    // 写入新值
    // 获取当前任务的端口
    mach_port_t task;
    uint64_t new_function_value = (uint64_t)loader_C;
    task_for_pid(mach_task_self(), getpid(), &task);
    // kern_return_t write_result = mach_vm_write(task, target_address, (vm_offset_t)&new_function_value, sizeof(new_function_value));
    // if (write_result != KERN_SUCCESS)
    // {
    //     os_log_error(log, "写入失败 (第一次): %s\n", mach_error_string(write_result));
    //     return; // 写入失败，退出
    // }

    target_address += 8;
    new_function_value = (uint64_t)loader_Croot;
    kern_return_t write_result = mach_vm_write(task, target_address, (vm_offset_t)&new_function_value, sizeof(new_function_value));
    if (write_result != KERN_SUCCESS)
    {
        os_log_error(log, "写入失败 (第二次): %s\n", mach_error_string(write_result));
        return;
    }
    os_log(log, "内存写入成功。");
}

// 编译命令:
// cc -o dontstarve_steam_inject.dylib lapi.c lcode.c ldebug.c ldo.c ldump.c lfunc.c lgc.c llex.c lmem.c lobject.c lopcodes.c lparser.c lstate.c lstring.c ltable.c ltm.c lundump.c lvm.c lzio.c lauxlib.c lbaselib.c ldblib.c liolib.c lmathlib.c loslib.c ltablib.c lstrlib.c loadlib.c linit.c inject_mac.c -shared -DLUA_USE_LINUX -lreadline -arch x86_64 -O2
// gcc main.c -o main
// DYLD_INSERT_LIBRARIES=./inject.so ./main

// 0f 1f 00 45 33 c0 48 8b d0 48 8b cb

// 4A 8B 34 3B 4C 89 F7 31 D2
// cc -o lua2 lapi.c lcode.c ldebug.c ldo.c ldump.c lfunc.c lgc.c llex.c lmem.c lobject.c lopcodes.c lparser.c lstate.c lstring.c ltable.c ltm.c lundump.c lvm.c lzio.c lauxlib.c lbaselib.c ldblib.c liolib.c lmathlib.c loslib.c ltablib.c lstrlib.c loadlib.c linit.c inject_mac.c lua.c -DLUA_USE_LINUX -lreadline -arch x86_64 -O2
